package com.example.lotterysystem.service.impl;

import com.example.lotterysystem.common.errorcode.ServiceErrorCodeConstants;
import com.example.lotterysystem.common.exception.ServiceException;
import com.example.lotterysystem.common.utils.JacksonUtil;
import com.example.lotterysystem.common.utils.RedisUtil;
import com.example.lotterysystem.controller.param.DrawPrizeParam;
import com.example.lotterysystem.controller.param.ShowWinningRecordsParam;
import com.example.lotterysystem.dao.dataobject.*;
import com.example.lotterysystem.dao.mapper.*;
import com.example.lotterysystem.service.DrawPrizeService;
import com.example.lotterysystem.service.dto.WinningRecordDTO;
import com.example.lotterysystem.service.enums.ActivityPrizeStatusEnum;
import com.example.lotterysystem.service.enums.ActivityPrizeTiersEnum;
import com.example.lotterysystem.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;

import static com.example.lotterysystem.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.example.lotterysystem.common.config.DirectRabbitConfig.ROUTING;

/**
 * Created with IntelliJ IDEA.
 * Description: 异步抽奖接口实现
 * User: Spider-Man
 * Date: 2025-09-14
 * Time: 14:42
 */
@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static Logger logger = LoggerFactory.getLogger(DrawPrizeServiceImpl.class);
    private final Long WINNING_RECORDS_TIMEOUT = 60*60*24*2L;
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private WinningRecordMapper winningRecordMapper;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public void drawPrize(DrawPrizeParam param) {
        Map<String, String> map = new HashMap<>();//消息
        map.put("messageId", String.valueOf(UUID.randomUUID()));//消息id
        map.put("messageData", JacksonUtil.writeValueAsString(param));
        //发送消息: 交换机 --> 绑定的key ---> 消息体
        rabbitTemplate.convertAndSend(EXCHANGE_NAME, ROUTING, map); //转换消息格式并发送
        logger.info("mq消息发送成功, map={}", JacksonUtil.writeValueAsString(map));
    }

    //抽奖请求校验
    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        //通过活动id,在数据库当中获取到数据是否存在
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        //奖品是否存在可以在activity_prize中查看到
        //在保存activity信息的时候做了本地事务,保证了数据的一致性.
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(
                param.getActivityId(),param.getPrizeId());
        //1.活动或奖品是否存在
        if(null == activityDO || null == activityPrizeDO) {
            //活动不存在
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_ISEMPTY);
            logger.info("校验抽奖请求失败!",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_ISEMPTY.getMsg());
            return false;
        }
        //2.活动是否有效
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            //活动失效
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.info("校验抽奖请求失败!",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_ISEMPTY.getMsg());

            return false;
        }
        //3.活动奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            logger.info("校验抽奖请求失败!",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_ISEMPTY.getMsg());

            return false;

        }

        //4.中奖人数与奖品数量不一致
        if(activityPrizeDO.getPrizeAmount() != param.getWinnerList().size()) {
           // throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            logger.info("校验抽奖请求失败!",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_ISEMPTY.getMsg());

            return false;

        }
        return true;
    }

    //保存中奖信息
    @Override
    public List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        //1.查询相关的信息(活动,人员,奖品信息)
        /**
         * 活动信息查询
         */
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());
        /**
         * 人员信息查询
         */
        List<UserDO> userDOlist = userMapper.batchSelectByIds(param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId).collect(Collectors.toList()));
        /**
         * 奖品信息查询
         */
        PrizeDO prizeDO = prizeMapper.selectById(param.getPrizeId());
        /**
         * 活动奖品关联表
         * 使用活动id和奖品id定位到唯一数据
         */
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        //2.构造中奖者记录
        List<WinningRecordDO> winningRecordDOList = userDOlist.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                   /* winnerRecordDO.setId();
                    winnerRecordDO.setGmtCreate();
                    winnerRecordDO.setGmtModified(); sql当中的自增属性以及自动生成的*/
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);//存入中奖信息到中奖表当中
        //3.缓存中奖者记录(前端需要展示中奖信息)
        //1).奖品的中奖记录(奖品被谁抽取) --- key:activityId_prizeId  value:winningRecordDOList(奖品维度的名单))
        cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),
                winningRecordDOList,
                WINNING_RECORDS_TIMEOUT);
        //2).活动的中奖记录(活动中谁中奖) ---- key:activityId  value:winningRecordDOList(根据活动查询的))
        //活动完成之后,才去存放活动维度中奖记录
        if(activityDO.getStatus().equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            // 先查询到活动维度的全量记录
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId()); //返回一批活动
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                     allList,WINNING_RECORDS_TIMEOUT);
        }
        return winningRecordDOList;
    }

    /**
     * 删除中奖记录
     * @param activityId
     * @param prizeId
     */
    @Override
    public void deleteRecords(Long activityId,Long prizeId) {
        if(activityId == null) {
            logger.warn("要删除的中奖记录相关的活动id或奖品id为空!");
            return;
        }
        //删除数据表
        winningRecordMapper.deleteRecords(activityId,prizeId);
        //删除缓存
        if(null != prizeId) {
            deleteWinningRecords(activityId+"_"+prizeId); //删除奖品维度的
        }
        // 无论是否传递了prizeid，都需要删除活动维度下的缓存
        // 如果传递了prizeid,证明奖品未抽奖,必须删除活动维度的缓存记录
        //如果没有传递,则只删除活动维度的记录
        deleteWinningRecords(String.valueOf(activityId));
    }

    //获取中奖记录
    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        // 查询redis: 奖品、活动
        String key = null == param.getPrizeId()
                ? String.valueOf(param.getActivityId())
                : param.getActivityId() + "_" + param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList = getWinningRecords(key);
        if (!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordDTOList(winningRecordDOList);
        }
        // 如果redis不存在，查库
        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(
                param.getActivityId(), param.getPrizeId());
        // 存放记录到redis
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("查询的中奖记录为空！param:{}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        cacheWinningRecords(key, winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winningRecordDOList);
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinningRecordDO> winningRecordDOList) {
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        return winningRecordDOList.stream()
                .map(winningRecordDO -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winningRecordDO.getWinnerId());
                    winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(
                            ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return winningRecordDTO;
                }).collect(Collectors.toList());
    }

    /**
     * 从缓存当中删除奖品维度下的数据
     * @param key
     */
    private void deleteWinningRecords(String key) {
        try {
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                //存在,进行删除
                redisUtil.del(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e) {
            logger.error("删除中奖记录缓存异常,key:{}",key);
        }

    }
    /**
     * 缓存中奖记录
     * @param key
     * @param winningRecordDOList 缓存内容
     * @param time 缓存存放的时间
     */
    private void cacheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, Long time) {
        String str = "";
        try {
            if(!StringUtils.hasText(key) ||
                    CollectionUtils.isEmpty(winningRecordDOList)) {
                logger.warn("要缓存内容存在异常!key:{},value:{}"
                        ,key,JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }
            str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX + key, str, time);
        }catch (Exception e) {
            logger.error("缓存中奖记录异常! key:{},value:{}",WINNING_RECORDS_PREFIX+key,str);
        }
    }


    /**
     * 从缓存当做获取数据
     * @param key
     * @return
     */
    private List<WinningRecordDO> getWinningRecords(String key) {
       try {
           if(!StringUtils.hasText(key)) {
               logger.warn("要从缓存中查询的中奖记录的key为空!");
               return Arrays.asList();
           }
           String str = redisUtil.get(WINNING_RECORDS_PREFIX + key);
           //判断一下key
           if(!StringUtils.hasText(str)) {
               return Arrays.asList();
           }
           return JacksonUtil.readListValue(str, WinningRecordDO.class);
       } catch (Exception e) {
           logger.error("从缓存中查询中奖记录异常! key:{}",
                   WINNING_RECORDS_PREFIX + key);
           return Arrays.asList();
       }
    }
}
